package com.lanzhou.yuanfen.security.filter;

import com.alibaba.fastjson.JSON;
import com.lanzhou.yuanfen.security.token.QQAuthenticationToken;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Matcher;

import static java.util.regex.Pattern.compile;

/**
 * AbstractAuthenticationProcessingFilter这个过滤器在前面一节介绍过，
 * 是UsernamePasswordAuthenticationFilter的父类，我们的IpAuthenticationProcessingFilter也继承了它
 * 构造器中传入了/emailVerify作为IP登录的端点
 * attemptAuthentication()方法中加载请求的IP地址，之后交给内部的AuthenticationManager去认证
 *
 * @author Administrator
 */
@Slf4j
public class QQAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

    /**
     * 拦截路径
     */
    private final static String URL = "/qqLogin";


    /**
     * Code 拦截参数
     */
    private final static String CODE = "code";

    /**
     * 获取 Token 的 API
     */
    private final static String ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token";

    /**
     * grant_type 由腾讯提供
     */
    private final static String GRANT_TYPE = "authorization_code";

    /**
     * client_id 由腾讯提供
     */
    private static final String CLIENT_ID = "101848364";

    /**
     * client_secret 由腾讯提供
     */
    private final static String CLIENT_SECRET = "eb2b63999cfcc6831c99ab362970d6a3";

    /**
     * redirect_uri 腾讯回调地址
     */
    private final static String REDIRECT_URI = "http://yuangfeng.top/client";

    /**
     * 获取 OpenID 的 API 地址
     */
    private final static String OPENID_URI = "https://graph.qq.com/oauth2.0/me?access_token=";

    /**
     * 获取 token 的地址拼接
     */
    private final static String TOKEN_ACCESS_API = "%s?grant_type=%s&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s";

    /**
     * QQ 过滤器登入策略
     *
     * @param sessionStrategy 自定义会话策略
     */
    public QQAuthenticationProcessingFilter(SessionAuthenticationStrategy sessionStrategy) {
        super(new AntPathRequestMatcher(URL, "GET"));
        super.setSessionAuthenticationStrategy(sessionStrategy);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        String code = request.getParameter(CODE);
        String tokenAccessApi = String.format(TOKEN_ACCESS_API, ACCESS_TOKEN_URL,
                GRANT_TYPE, CLIENT_ID, CLIENT_SECRET, code, REDIRECT_URI);
        QQToken qqToken = this.getToken(tokenAccessApi);
        System.out.println(JSON.toJSONString(qqToken));
        if (qqToken != null) {
            String openId = getOpenId(qqToken.getAccessToken());
            if (openId != null) {
                // 返回验证结果
                log.info("当前认证管理器为: {}", this.getAuthenticationManager().getClass().getName());
                return this.getAuthenticationManager().authenticate(new QQAuthenticationToken(qqToken.getAccessToken(), openId));
            }
        }
        return null;
    }

    private QQToken getToken(String tokenAccessApi) throws IOException {
        Document document = Jsoup.connect(tokenAccessApi).get();
        String tokenResult = document.text();
        String[] results = tokenResult.split("&");
        if (results.length == 3) {
            QQToken token = new QQToken();
            String accessToken = results[0].replace("access_token=", "");
            int expiresIn = Integer.valueOf(results[1].replace("expires_in=", ""));
            String refreshToken = results[2].replace("refresh_token=", "");
            token.setAccessToken(accessToken);
            token.setExpiresIn(expiresIn);
            token.setRefreshToken(refreshToken);
            return token;
        }
        return null;
    }

    private String getOpenId(String accessToken) throws IOException {
        String url = OPENID_URI + accessToken;
        Document document = Jsoup.connect(url).get();
        String resultText = document.text();
        Matcher matcher = compile("\"openid\":\"(.*?)\"").matcher(resultText);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }


    /**
     * QQToken 数据
     */
    static class QQToken {

        /**
         * token
         */
        private String accessToken;

        /**
         * 有效期
         */
        private int expiresIn;

        /**
         * 刷新时用的 token
         */
        private String refreshToken;

        public String getAccessToken() {
            return accessToken;
        }

        public void setAccessToken(String accessToken) {
            this.accessToken = accessToken;
        }

        public int getExpiresIn() {
            return expiresIn;
        }

        public void setExpiresIn(int expiresIn) {
            this.expiresIn = expiresIn;
        }

        public String getRefreshToken() {
            return refreshToken;
        }

        public void setRefreshToken(String refreshToken) {
            this.refreshToken = refreshToken;
        }
    }

}
